import { ts,  NodePredicate } from 'qvog-dsl';
import { native, HomeCheckHint } from 'qvog-dsl';

const SEVERITY = 2;
const DOC_PATH = 'docs/class-literal-property-style.md';
const DESCRIPTION = 'Literals should be exposed using readonly fields.';

const ClassMember: NodePredicate<ts.ClassDeclaration> = {
  is(node: ts.Node): node is ts.ClassDeclaration {
    return ts.isClassDeclaration(node);
  }
};
export default native<HomeCheckHint>()()
  .match(ClassMember)
  .when((node: ts.ClassDeclaration): boolean => {
    let hasViolation = false;
    
    node.members.forEach(element => {
      
      if (ts.isGetAccessorDeclaration(element)) {
        if (!hasMatchingSetMethod(element)) {
          const body = element.body;
          if (body && body.statements.length > 0) {
            const [firstStatement] = body.statements;
            if (firstStatement.kind === ts.SyntaxKind.ReturnStatement) {
              const returnStmt = firstStatement as ts.ReturnStatement;
              if (returnStmt.expression && isSupportedLiteral(returnStmt.expression)) {
                hasViolation = true;
              }
            }
          }
        }
      }
      
      // examine property
      if (ts.isPropertyDeclaration(element)) {
        const modifiers = element.modifiers;
        if (modifiers && modifiers.some(mod => mod.kind === ts.SyntaxKind.ReadonlyKeyword)) {
          const initializer = element.initializer;
          if (initializer && isSupportedLiteral(initializer)) {
            hasViolation = true;
          }
        }
      }
    });
    
    return hasViolation;
  })
  .report({
    severity: SEVERITY,
    description: DESCRIPTION,
    docPath: DOC_PATH,
  });
function isSupportedLiteral(node: ts.Node): boolean {
  return (
    ts.isStringLiteral(node) ||
    ts.isNumericLiteral(node) ||
    ts.isNoSubstitutionTemplateLiteral(node) ||
    (ts.isTemplateExpression(node) && node.templateSpans.length === 0) ||
    node.kind === ts.SyntaxKind.TrueKeyword ||
    node.kind === ts.SyntaxKind.FalseKeyword ||
    node.kind === ts.SyntaxKind.BigIntLiteral ||
    node.kind === ts.SyntaxKind.NullKeyword ||
    node.kind === ts.SyntaxKind.UndefinedKeyword ||
    ts.isObjectLiteralExpression(node) ||
    ts.isArrayLiteralExpression(node)
  );
}

function hasMatchingSetMethod(method: ts.GetAccessorDeclaration): boolean {
  const parent = method.parent as ts.ClassDeclaration;
  return parent.members.some(
    member => ts.isSetAccessorDeclaration(member) && 
    member.name.getText().replace(/\/\*.*?\*\//g, '').replace(/^\[['"`]|['"`]\]$/g, '').trim() === 
    method.name.getText().replace(/\/\*.*?\*\//g, '').replace(/^\[['"`]|['"`]\]$/g, '').trim()
  );
}

